﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;

namespace SD.CoreLibrary.CronExpressionDescripter
{
    public interface ICronSegmentDescription
    {
        string AllDescription { get; set; }
        Func<string, string> SingleItemDescription { get; set; }
        Func<string, string> IntervalDescriptionFormat { get; set; }
        Func<string, string, string> BetweenDescriptionFormat { get; set; }
        Func<string, string> DescriptionFormat { get; set; }

    }

    public class CronDescriptionFunctor : ICronSegmentDescription
    {
        public string AllDescription { get { return DefaultAllDescription; } set { DefaultAllDescription = value; } }
        public Func<string, string> SingleItemDescription { get { return DefaultSingleItemDescription; } set { DefaultSingleItemDescription = value; } }
        public Func<string, string> IntervalDescriptionFormat { get { return DefaultIntervalDescriptionFormat; } set { DefaultIntervalDescriptionFormat = value; } }
        public Func<string, string, string> BetweenDescriptionFormat { get { return DefaultBetweenDescriptionFormat; } set { DefaultBetweenDescriptionFormat = value; } }
        public Func<string, string> DescriptionFormat { get { return DefaultDescriptionFormat; } set { DefaultDescriptionFormat = value; } }


        protected string DefaultAllDescription { get; set; }
        protected Func<string, string> DefaultSingleItemDescription { get; set; }
        protected Func<string, string> DefaultIntervalDescriptionFormat { get; set; }
        protected Func<string, string, string> DefaultBetweenDescriptionFormat { get; set; }
        protected Func<string, string> DefaultDescriptionFormat { get; set; }


        public CronDescriptionFunctor()
        { }

        public CronDescriptionFunctor(ICronSegmentDescription functorDescription)
            : this(functorDescription.AllDescription,
                functorDescription.SingleItemDescription,
                functorDescription.IntervalDescriptionFormat,
                functorDescription.BetweenDescriptionFormat,
                functorDescription.DescriptionFormat)
        { }

        public CronDescriptionFunctor(string allDescription,
                                     Func<string, string> singleItemDescription,
                                     Func<string, string> intervalDescriptionFormat,
                                     Func<string, string, string> betweenDescriptionFormat,
                                     Func<string, string> descriptionFormat)
        {
            AllDescription = allDescription;
            SingleItemDescription = singleItemDescription;
            IntervalDescriptionFormat = intervalDescriptionFormat;
            BetweenDescriptionFormat = betweenDescriptionFormat;
            DescriptionFormat = descriptionFormat;
        }
    }

    public class CronSecondsDescriptionFunctor : CronDescriptionFunctor
    {
        public CronSecondsDescriptionFunctor()
        {
            DefaultAllDescription = "every second";
            DefaultSingleItemDescription = (s => s.PadLeft(2, '0'));
            DefaultIntervalDescriptionFormat = (s => string.Format(" every {0} seconds", s));
            DefaultBetweenDescriptionFormat = ((s, p) => string.Format("seconds {0} through {1} past the minute", s, p));
            DefaultDescriptionFormat = (s => string.Format("at {0} seconds past the minute", s));

        }

        public CronSecondsDescriptionFunctor(string allDescription,
                             Func<string, string> singleItemDescription,
                             Func<string, string> intervalDescriptionFormat,
                             Func<string, string, string> betweenDescriptionFormat,
                             Func<string, string> descriptionFormat)
            : this()
        {
            DefaultAllDescription = allDescription;
            DefaultSingleItemDescription = singleItemDescription;
            DefaultIntervalDescriptionFormat = intervalDescriptionFormat;
            DefaultBetweenDescriptionFormat = betweenDescriptionFormat;
            DefaultDescriptionFormat = descriptionFormat;
        }

    }

    public class CronMinutesDescriptionFunctor : CronDescriptionFunctor
    {
        public CronMinutesDescriptionFunctor()
        {
            DefaultAllDescription = "every minute";
            DefaultSingleItemDescription = (s => s.PadLeft(2, '0'));
            DefaultIntervalDescriptionFormat = (s => string.Format(" every {0} minutes", s.PadLeft(2, '0')));
            DefaultBetweenDescriptionFormat = ((s, p) => string.Format("minutes {0} through {1} past the hour", s, p));
            DefaultDescriptionFormat = (s => s == "0" ? string.Empty : string.Format("at {0} minutes past the hour", s));

        }

        public CronMinutesDescriptionFunctor(string allDescription,
                             Func<string, string> singleItemDescription,
                             Func<string, string> intervalDescriptionFormat,
                             Func<string, string, string> betweenDescriptionFormat,
                             Func<string, string> descriptionFormat)
            : this()
        {
            DefaultAllDescription = allDescription;
            DefaultSingleItemDescription = singleItemDescription;
            DefaultIntervalDescriptionFormat = intervalDescriptionFormat;
            DefaultBetweenDescriptionFormat = betweenDescriptionFormat;
            DefaultDescriptionFormat = descriptionFormat;
        }

    }

    public class CronHoursDescriptionFunctor : CronDescriptionFunctor
    {
        public CronHoursDescriptionFunctor()
        {
            DefaultAllDescription = "every hour";
            DefaultSingleItemDescription = (s => CronExpressionDescriptor.FormatTime(true, s, "0"));
            DefaultIntervalDescriptionFormat = (s => string.Format(" every {0} hours", s.PadLeft(2, '0')));
            DefaultBetweenDescriptionFormat = ((s, p) => string.Format("between {0} and {1}", CronExpressionDescriptor.FormatTime(true, s, "0"), CronExpressionDescriptor.FormatTime(true, p, "0")));
            DefaultDescriptionFormat = (s => string.Format("at {0}", s));

        }

        public CronHoursDescriptionFunctor(string allDescription,
                             Func<string, string> singleItemDescription,
                             Func<string, string> intervalDescriptionFormat,
                             Func<string, string, string> betweenDescriptionFormat,
                             Func<string, string> descriptionFormat)
            : this()
        {
            DefaultAllDescription = allDescription;
            DefaultSingleItemDescription = singleItemDescription;
            DefaultIntervalDescriptionFormat = intervalDescriptionFormat;
            DefaultBetweenDescriptionFormat = betweenDescriptionFormat;
            DefaultDescriptionFormat = descriptionFormat;
        }

    }

    public class CronMonthsDescriptionFunctor : CronDescriptionFunctor
    {
        public CronMonthsDescriptionFunctor()
        {
            DefaultAllDescription = string.Empty;
            DefaultSingleItemDescription = (s => new DateTime(DateTime.Now.Year, Convert.ToInt32(s), 1).ToString("MMMM", new CultureInfo("en-US")));
            DefaultIntervalDescriptionFormat = (s => string.Format(" every {0} months", s));
            DefaultBetweenDescriptionFormat = ((s, p) => string.Format(" {0} through {1}", s, p));
            DefaultDescriptionFormat = (s => string.Format(", only in {0}", s));

        }

        public CronMonthsDescriptionFunctor(string allDescription,
                             Func<string, string> singleItemDescription,
                             Func<string, string> intervalDescriptionFormat,
                             Func<string, string, string> betweenDescriptionFormat,
                             Func<string, string> descriptionFormat)
            : this()
        {
            DefaultAllDescription = allDescription;
            DefaultSingleItemDescription = singleItemDescription;
            DefaultIntervalDescriptionFormat = intervalDescriptionFormat;
            DefaultBetweenDescriptionFormat = betweenDescriptionFormat;
            DefaultDescriptionFormat = descriptionFormat;
        }

    }

    public class CronDayOfWeeksDescriptionFunctor : CronDescriptionFunctor
    {
        public CronDayOfWeeksDescriptionFunctor()
        {
            DefaultAllDescription = ", every day";
            DefaultSingleItemDescription = (s => CronExpressionDescriptor.FormatDayOfTheWeek(s));
            DefaultIntervalDescriptionFormat = (s => string.Format(" every {0} days of the week", s));
            DefaultBetweenDescriptionFormat = ((s, p) => string.Format(" {0} through {1}", CronExpressionDescriptor.FormatDayOfTheWeek(s), CronExpressionDescriptor.FormatDayOfTheWeek(p)));
            DefaultDescriptionFormat = (s => string.Format(", on {0}", s));

        }

        public CronDayOfWeeksDescriptionFunctor(string allDescription,
                             Func<string, string> singleItemDescription,
                             Func<string, string> intervalDescriptionFormat,
                             Func<string, string, string> betweenDescriptionFormat,
                             Func<string, string> descriptionFormat)
            : this()
        {
            DefaultAllDescription = allDescription;
            DefaultSingleItemDescription = singleItemDescription;
            DefaultIntervalDescriptionFormat = intervalDescriptionFormat;
            DefaultBetweenDescriptionFormat = betweenDescriptionFormat;
            DefaultDescriptionFormat = descriptionFormat;
        }

    }

    public class CronDayOfMonthsDescriptionFunctor : CronDescriptionFunctor
    {
        public CronDayOfMonthsDescriptionFunctor()
        {
            DefaultAllDescription = ", every day";
            DefaultSingleItemDescription = (s => s);
            DefaultIntervalDescriptionFormat = (s => s == "1" ? " every day" : string.Format(" every {0} days",s));
            DefaultBetweenDescriptionFormat = ((s, p) => string.Format(" between day {0} and {1} of the month", s, p));
            DefaultDescriptionFormat = (s => string.Format(", on day {0} of the month", s));

        }

        public CronDayOfMonthsDescriptionFunctor(string allDescription,
                             Func<string, string> singleItemDescription,
                             Func<string, string> intervalDescriptionFormat,
                             Func<string, string, string> betweenDescriptionFormat,
                             Func<string, string> descriptionFormat)
            : this()
        {
            DefaultAllDescription = allDescription;
            DefaultSingleItemDescription = singleItemDescription;
            DefaultIntervalDescriptionFormat = intervalDescriptionFormat;
            DefaultBetweenDescriptionFormat = betweenDescriptionFormat;
            DefaultDescriptionFormat = descriptionFormat;
        }

    }

    public class CronYearsDescriptionFunctor : CronDescriptionFunctor
    {
        public CronYearsDescriptionFunctor()
        {
            DefaultAllDescription = string.Empty;
            DefaultSingleItemDescription = (s => new DateTime(Convert.ToInt32(s), 1, 1).ToString("yyyy"));
            DefaultIntervalDescriptionFormat = (s => string.Format(" every {0} years", s));
            DefaultBetweenDescriptionFormat = ((s, p) => string.Format(" from {0} through {1}", s, p));
            DefaultDescriptionFormat = (s => string.Format(", only in {0}", s));

        }

        public CronYearsDescriptionFunctor(string allDescription,
                             Func<string, string> singleItemDescription,
                             Func<string, string> intervalDescriptionFormat,
                             Func<string, string, string> betweenDescriptionFormat,
                             Func<string, string> descriptionFormat)
            : this()
        {
            DefaultAllDescription = allDescription;
            DefaultSingleItemDescription = singleItemDescription;
            DefaultIntervalDescriptionFormat = intervalDescriptionFormat;
            DefaultBetweenDescriptionFormat = betweenDescriptionFormat;
            DefaultDescriptionFormat = descriptionFormat;
        }

    }


}
